﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Noris.Tools.TraceVisualiser.Interface;
using Noris.Tools.TraceVisualiser.TraceData;
using Noris.Tools.TraceVisualiser.Components;
using System.Drawing;

namespace Noris.Tools.TraceVisualiser.Green.AplServer
{
    /// <summary>
    /// Služba pro dohledání informací o konkrétní události
    /// </summary>
    public class TraceSupply : ITraceInfoSupply
    {
        #region ITraceInfoSupply + IPlugin members
        bool IPlugin.Active { get { return true; } }
        void ITraceInfoSupply.SupplyInfo(TraceInfoSupplyArgs args) { this._SupplyInfo(args); }
        #endregion
        #region Řídící procesy
        /// <summary>
        /// Dohledá informace k události uvedené v TraceInfoSupplyArgs
        /// </summary>
        /// <param name="args"></param>
        private void _SupplyInfo(TraceInfoSupplyArgs args)
        {
            // args obsahuje UserData, kde je popsána událost, k níž došlo
            //supplyArgs.SupplyData.Add("SpID", spidInfo.SpID);
            //supplyArgs.SupplyData.Add("SpIdNumber", spidInfo.SpIdNumber);
            //supplyArgs.SupplyData.Add("DeadlockTime", spidInfo.Parent.DeadlockTime);
            //supplyArgs.SupplyData.Add("MaxTime", spidInfo.Parent.MaxTime);
            //supplyArgs.SupplyData.Add("LastTransactionStarted", spidInfo.LastTransactionStarted);
            //supplyArgs.SupplyData.Add("IsWaiterToAnyResource", spidInfo.IsWaiterToAnyResource);
            //supplyArgs.SupplyData.Add("IsVictim", spidInfo.IsVictim);

            string supplyRequest = args.SupplyRequest;
            if (supplyRequest == null) return;
            if (supplyRequest == TraceConstants.TRACESUPPLY_INFO_DEADLOCK)
                this._SupplyInfoForDeadlock(args);

        }
        /// <summary>
        /// Dohledá informace k události typu Deadlock
        /// </summary>
        /// <param name="args"></param>
        private void _SupplyInfoForDeadlock(TraceInfoSupplyArgs args)
        {
            ProcessInfo processInfo = this._FindDataProcess(args);
            args.ResultInfoList.Add(this._FindUserLoginRow(args, processInfo));
            args.ResultInfoList.AddRange(this._FindCowleyList(args, processInfo));
            args.ResultInfoList.AddRange(this._FindSqlCmdList(args, processInfo, 7));
        }
        #endregion
        #region DataMinning z TraceData
        /// <summary>
        /// Najde a vrátí proces pocházející z aplikačního serveru z datové základy TraceData, 
        /// který odpovídá časově deadlocku a který v dané době používá transakci nebo SQL příkazy pro SPID daného SQL procesu.
        /// </summary>
        /// <param name="traceData">Data všech trace, základní objekt</param>
        /// <param name="spidInfo">Data o SQL procesu</param>
        /// <returns></returns>
        private ProcessInfo _FindDataProcess(TraceInfoSupplyArgs args)
        {
            DateTime deadlockTime = args.SupplyData.GetDateTime("DeadlockTime", DateTime.MinValue);
            int spId = args.SupplyData.GetInt32("SpIdNumber", 0);
            return args.Main.FindFirstOrDefaultProcessInfoWithEventInTime(deadlockTime, TraceEventTimeSelect.VisualTime, ev => this._IsEventSqlOnSpid(ev, spId));
        }
        /// <summary>
        /// Vytvoří a vrátí řádek do listu obsahující login.
        /// </summary>
        /// <param name="processInfo"></param>
        /// <param name="spidInfo"></param>
        /// <returns></returns>
        private TraceInfoSupplyItem _FindUserLoginRow(TraceInfoSupplyArgs args, ProcessInfo processInfo)
        {
            if (processInfo != null)
                return new TraceInfoSupplyItem() { Name = "Login", Value = processInfo.HomeNode.TextFirst, BackColor = Color.LightGreen, GroupKey = TraceConstants.TRACESUPPLY_GROUPKEY_HEADER };
            else
                return new TraceInfoSupplyItem() { Name = "Login", Value = "???", BackColor = Color.LightYellow, GroupKey = TraceConstants.TRACESUPPLY_GROUPKEY_HEADER };
        }
        /// <summary>
        /// Vrátí seznam funkcí pro daného uživatele 
        /// </summary>
        /// <param name="processInfo">Data o procesu z trace dat, který nese aplikační data uživatele Greenu, který odpovídá procesu SQL serveru</param>
        /// <param name="spidInfo">Data o SQL procesu</param>
        /// <returns></returns>
        private List<TraceInfoSupplyItem> _FindCowleyList(TraceInfoSupplyArgs args, ProcessInfo processInfo)
        {
            List<TraceInfoSupplyItem> rows = new List<TraceInfoSupplyItem>();
            if (processInfo != null)
            {
                DateTime deadlockTime = args.SupplyData.GetDateTime("DeadlockTime", DateTime.MinValue);
                List<EventInfo> events = processInfo.Events.Where(ev => this._IsEventNrsCowley(ev, deadlockTime)).ToList();
                if (events != null)
                {
                    events.Sort(EventInfo.CompareByBeginSubGroupEnd);            // Setřídit podle času počátku
                    events.Reverse();                                            // Do seznamu budeme položky dávat "první = dospodu"
                    foreach (EventInfo eventInfo in events)
                    {
                        EventCowleyItem eventCowley = eventInfo.EventItem as EventCowleyItem;
                        if (eventCowley != null)
                        {
                            IDataElement element = eventInfo as IDataElement;    // Každý EventInfo je současně i elementem, ale z toho pohledu má jiné vlastnosti (má explicitní implementace interface)
                            TraceInfoSupplyItem row = new TraceInfoSupplyItem()
                            {
                                GroupKey = PROC_GROUP_COWLEY,
                                GroupHeader = "Cowley",
                                Name = eventCowley.CowleyFunctionNumber.ToString(),
                                Value = eventCowley.CowleyName,
                                BackColor = element.BackColor,
                                ToolTipText = element.ToolTipInfo
                            };
                            rows.Add(row);
                        }
                    }
                }
            }
            return rows;
        }
        /// <summary>
        /// Najde a vrátí seznam posledních SQL příkazů v daném procesu před okamžikem deadlocku
        /// </summary>
        /// <param name="processInfo"></param>
        /// <param name="spidInfo"></param>
        /// <param name="count"></param>
        /// <returns></returns>
        private List<TraceInfoSupplyItem> _FindSqlCmdList(TraceInfoSupplyArgs args, ProcessInfo processInfo, int count)
        {
            List<TraceInfoSupplyItem> rows = new List<TraceInfoSupplyItem>();
            if (processInfo != null)
            {
                DateTime deadlockTime = args.SupplyData.GetDateTime("DeadlockTime", DateTime.MinValue);
                int spId = args.SupplyData.GetInt32("SpIdNumber", 0);
                List<EventInfo> events = processInfo.Events.Where(ev => this._IsEventSqlCommand(ev, deadlockTime, spId)).ToList();

                // Setřídit položky, takže na začátku jsou nejstarší:
                events.Sort(EventInfo.CompareByBeginSubGroupEnd);

                // Nyní najdu index takové pozice, abych od této pozice do konce seznamu měl (count) položek:
                int eventCnt = events.Count;
                int first = (eventCnt <= count ? 0 : eventCnt - count);
                for (int i = first; i < eventCnt; i++)
                {
                    EventSqlItem eventSqlCmd = events[i].EventItem as EventSqlItem;
                    if (eventSqlCmd != null)
                    {
                        IDataElement element = events[i] as IDataElement;    // Každý EventInfo je současně i elementem, ale z toho pohledu má jiné vlastnosti (má explicitní implementace interface)
                        TraceInfoSupplyItem row = new TraceInfoSupplyItem()
                        {
                            GroupKey = PROC_GROUP_SQLCMD,
                            GroupHeader = "SQL commands",
                            Name = eventSqlCmd.Time.MilliSeconds.ToString("### ##0") + " milisec", /*eventSqlCmd.Time.Begin.ToString("HH:mm:ss.fff") + " + " + eventSqlCmd.Time.MilliSeconds.ToString("### ##0") + " ms"*/
                            Value = eventSqlCmd.SqlCommand,
                            BackColor = element.BackColor,
                            ToolTipText = element.ToolTipInfo
                        };
                        rows.Add(row);
                    }
                }
            }
            return rows;
        }
        /// <summary>
        /// Určí, zda daný event pochází z aplikačního serveru a zda má spojitost s SPID procesem daného čísla
        /// </summary>
        /// <param name="eventInfo"></param>
        /// <param name="spid"></param>
        /// <returns></returns>
        private bool _IsEventSqlOnSpid(EventInfo eventInfo, int spId)
        {
            EventItem eventItem = eventInfo.EventItem as EventItem;
            if (eventItem == null) return false;

            if (eventItem is EventTransItem)
            {
                EventTransItem eventTrans = eventItem as EventTransItem;
                return (eventTrans.SqlPId == spId);
            }
            else if (eventItem is EventSqlItem)
            {
                EventSqlItem eventSqlCmd = eventItem as EventSqlItem;
                return (eventSqlCmd.SqlPId == spId);
            }
            return false;
        }
        /// <summary>
        /// Určí, zda daný event pochází z aplikačního serveru a zda je to NrsCowley a zda se nachází v daném čase
        /// </summary>
        /// <param name="eventInfo"></param>
        /// <param name="eventTime"></param>
        /// <returns></returns>
        private bool _IsEventNrsCowley(EventInfo eventInfo, DateTime eventTime)
        {
            // Čas:
            if (!eventInfo.TimeBySelect(TraceEventTimeSelect.VisualTime).Contain(eventTime))
                return false;

            // Viditelnost:
            if (!((IDataElement)(eventInfo.EventItem)).ElementVisible)
                return false;

            // Aplikační server Green, NrsCowley:
            return (eventInfo.EventItem is EventCowleyItem);
        }
        /// <summary>
        /// Určí, zda daný event pochází z aplikačního serveru a zda je to SqlCommand a zda se nachází v čase / před časem deadlocku a ve správném SPID:
        /// </summary>
        /// <param name="eventInfo"></param>
        /// <param name="spidInfo"></param>
        /// <returns></returns>
        private bool _IsEventSqlCommand(EventInfo eventInfo, DateTime eventTime, int spId)
        {
            // Čas:
            if (eventInfo.TimeBySelect(TraceEventTimeSelect.VisualTime).Begin > eventTime)
                return false;

            // Viditelnost:
            if (!((IDataElement)(eventInfo.EventItem)).ElementVisible)
                return false;

            // Aplikační server Green, EventSql:
            EventSqlItem eventSqlCmd = eventInfo.EventItem as EventSqlItem;
            if (eventSqlCmd == null) return false;

            // SPID:
            return (eventSqlCmd.SqlPId == spId);
        }
        private const string PROC_GROUP_COWLEY = "InfoCowley";
        private const string PROC_GROUP_SQLCMD = "InfoSqlCmd";
        #endregion
    }
}
